/* UNIX V7 source code: see /COPYRIGHT or www.tuhs.org for details. */

#include "dextern.h"
#include "prototype.h"

	/* important local variables */
int lastred;			/* the number of the last reduction of a state */
int defact[NSTATES];		/* the default actions of states */

void output(void)
{				/* print the output for the states */

	int i, k, c;
	register struct wset *u, *v;

	fprintf(ftable, "short yyexca[] ={\n");

	SLOOP(i) {		/* output the stuff for state i */
		nolook = !(tystate[i] == MUSTLOOKAHEAD);
		closure(i);
		/* output actions */
		nolook = 1;
		aryfil(temp1, ntokens + nnonter + 1, 0);
		WSLOOP(wsets, u) {
			c = *(u->pitem);
			if (c > 1 && c < NTBASE && temp1[c] == 0) {
				WSLOOP(u, v) {
					if (c == *(v->pitem))
						putitem(v->pitem + 1, NULL);
				}
				temp1[c] = state(c);
			} else if (c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0) {
				temp1[c + ntokens] = amem[indgo[i] + c];
			}
		}

		if (i == 1)
			temp1[1] = ACCEPTCODE;

		/* now, we have the shifts; look at the reductions */

		lastred = 0;
		WSLOOP(wsets, u) {
			c = *(u->pitem);
			if (c <= 0) {	/* reduction */
				lastred = -c;
				TLOOP(k) {
					if (BIT(u->ws.lset, k)) {
						if (temp1[k] == 0)
							temp1[k] = c;
						else if (temp1[k] < 0) {	/* reduce/reduce conflict */
							if (foutput != NULL)
								fprintf(foutput, "\n%d: reduce/reduce conflict (red'ns %d and %d ) on %s", i, -temp1[k], lastred, symnam(k));
							if (-temp1[k] > lastred)
								temp1[k] = -lastred;
							++zzrrconf;
						} else {	/* potential shift/reduce conflict */
							precftn(lastred, k, i);
						}
					}
				}
			}
		}
		wract(i);
	}

	fprintf(ftable, "\t};\n");

	wdef("YYNPROD", nprod);

}

int pkdebug = 0;

int apack(int *p, int n)
{				/* pack state i from temp1 into amem */
	int off;
	int *pp, *qq, *rr;
	int *q, *r;

	/* we don't need to worry about checking because we
	   we will only look entries known to be there... */

	/* eliminate leading and trailing 0's */

	q = p + n;
	for (pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)	/* VOID */
		;
	if (pp > q)
		return (0);	/* no actions */
	p = pp;

	/* now, find a place for the elements from p to q, inclusive */

	r = &amem[ACTSIZE - 1];
	for (rr = amem; rr <= r; ++rr, ++off) {	/* try rr */
		for (qq = rr, pp = p; pp <= q; ++pp, ++qq) {
			if (*pp != 0) {
				if (*pp != *qq && *qq != 0)
					goto nextk;
			}
		}

		/* we have found an acceptable k */

		if (pkdebug && foutput != NULL)
			fprintf(foutput, "off = %d, k = %d\n", off, (int)(rr - amem));

		for (qq = rr, pp = p; pp <= q; ++pp, ++qq) {
			if (*pp) {
				if (qq > r)
					error("action table overflow");
				if (qq > memp)
					memp = qq;
				*qq = *pp;
			}
		}
		if (pkdebug && foutput != NULL) {
			for (pp = amem; pp <= memp; pp += 10) {
				fprintf(foutput, "\t");
				for (qq = pp; qq <= pp + 9; ++qq)
					fprintf(foutput, "%d ", *qq);
				fprintf(foutput, "\n");
			}
		}
		return (off);

	      nextk:;
	}
	error("no space in action table");
	/* NOTREACHED */
}

void go2out(void)
{				/* output the gotos for the nontermninals */
	int i, j, k, best, count, cbest, times;

	fprintf(ftemp, "$\n");	/* mark begining of gotos */

	for (i = 1; i <= nnonter; ++i) {
		go2gen(i);

		/* find the best one to make default */

		best = -1;
		times = 0;

		for (j = 0; j <= nstate; ++j) {	/* is j the most frequent */
			if (tystate[j] == 0)
				continue;
			if (tystate[j] == best)
				continue;

			/* is tystate[j] the most frequent */

			count = 0;
			cbest = tystate[j];

			for (k = j; k <= nstate; ++k)
				if (tystate[k] == cbest)
					++count;

			if (count > times) {
				best = cbest;
				times = count;
			}
		}

		/* best is now the default entry */

		zzgobest += (times - 1);
		for (j = 0; j <= nstate; ++j) {
			if (tystate[j] != 0 && tystate[j] != best) {
				fprintf(ftemp, "%d,%d,", j, tystate[j]);
				zzgoent += 1;
			}
		}

		/* now, the default */

		zzgoent += 1;
		fprintf(ftemp, "%d\n", best);

	}



}

int g2debug = 0;

void go2gen(int c)
{				/* output the gotos for nonterminal c */

	int i, work, cc;
	struct item *p, *q;


	/* first, find nonterminals with gotos on c */

	aryfil(temp1, nnonter + 1, 0);
	temp1[c] = 1;

	work = 1;
	while (work) {
		work = 0;
		PLOOP(0, i) {
			if ((cc = prdptr[i][1] - NTBASE) >= 0) {	/* cc is a nonterminal */
				if (temp1[cc] != 0) {	/* cc has a goto on c */
					cc = *prdptr[i] - NTBASE;	/* thus, the left side of production i does too */
					if (temp1[cc] == 0) {
						work = 1;
						temp1[cc] = 1;
					}
				}
			}
		}
	}

	/* now, we have temp1[c] = 1 if a goto on c in closure of cc */

	if (g2debug && foutput != NULL) {
		fprintf(foutput, "%s: gotos on ", nontrst[c].name);
		NTLOOP(i) if (temp1[i])
			fprintf(foutput, "%s ", nontrst[i].name);
		fprintf(foutput, "\n");
	}

	/* now, go through and put gotos into tystate */

	aryfil(tystate, nstate, 0);
	SLOOP(i) {
		ITMLOOP(i, p, q) {
			if ((cc = *p->pitem) >= NTBASE) {
				if (temp1[cc -= NTBASE]) {	/* goto on c is possible */
					tystate[i] = amem[indgo[i] + c];
					break;
				}
			}
		}
	}
}

void precftn(int r, int t, int s)
{				/* decide a shift/reduce conflict by precedence. */
	/* r is a rule number, t a token number */
	/* the conflict is in state s */
	/* temp1[t] is changed to reflect the action */

	int lp, lt, action;

	lp = levprd[r];
	lt = toklev[t];
	if (PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
		/* conflict */
		if (foutput != NULL)
			fprintf(foutput, "\n%d: shift/reduce conflict (shift %d, red'n %d) on %s", s, temp1[t], r, symnam(t));
		++zzsrconf;
		return;
	}
	if (PLEVEL(lt) == PLEVEL(lp))
		action = ASSOC(lt);
	else if (PLEVEL(lt) > PLEVEL(lp))
		action = RASC;	/* shift */
	else
		action = LASC;	/* reduce */

	switch (action) {

	case BASC:		/* error action */
		temp1[t] = ERRCODE;
		return;

	case LASC:		/* reduce */
		temp1[t] = -r;
		return;

	}
}

void wract(int i)
{				/* output state i */
	/* temp1 has the actions, lastred the default */
	int p, p0, p1;
	int ntimes, tred, count, j;
	int flag;

	/* find the best choice for lastred */

	lastred = 0;
	ntimes = 0;
	TLOOP(j) {
		if (temp1[j] >= 0)
			continue;
		if (temp1[j] + lastred == 0)
			continue;
		/* count the number of appearances of temp1[j] */
		count = 0;
		tred = -temp1[j];
		levprd[tred] |= REDFLAG;
		TLOOP(p) {
			if (temp1[p] + tred == 0)
				++count;
		}
		if (count > ntimes) {
			lastred = tred;
			ntimes = count;
		}
	}

	/* for error recovery, arrange that, if there is a shift on the */
	/* error recovery token, `error', that the default be the error action */
	if (temp1[1] > 0)
		lastred = 0;

	/* clear out entries in temp1 which equal lastred */
	TLOOP(p) if (temp1[p] + lastred == 0)
		temp1[p] = 0;

	wrstate(i);
	defact[i] = lastred;

	flag = 0;
	TLOOP(p0) {
		if ((p1 = temp1[p0]) != 0) {
			if (p1 < 0) {
				p1 = -p1;
				goto exc;
			} else if (p1 == ACCEPTCODE) {
				p1 = -1;
				goto exc;
			} else if (p1 == ERRCODE) {
				p1 = 0;
				goto exc;
			      exc:
				if (flag++ == 0)
					fprintf(ftable, "-1, %d,\n", i);
				fprintf(ftable, "\t%d, %d,\n", tokset[p0].value, p1);
				++zzexcp;
			} else {
				fprintf(ftemp, "%d,%d,", tokset[p0].value, p1);
				++zzacent;
			}
		}
	}
	if (flag) {
		defact[i] = -2;
		fprintf(ftable, "\t-2, %d,\n", lastred);
	}
	fprintf(ftemp, "\n");
	return;
}

void wrstate(int i)
{				/* writes state i */
	int j0, j1;
	struct item *pp, *qq;
	struct wset *u;

	if (foutput == NULL)
		return;
	fprintf(foutput, "\nstate %d\n", i);
	ITMLOOP(i, pp, qq) fprintf(foutput, "\t%s\n", writem(pp->pitem));
	if (tystate[i] == MUSTLOOKAHEAD) {
		/* print out empty productions in closure */
		WSLOOP(wsets + (pstate[i + 1] - pstate[i]), u) {
			if (*(u->pitem) < 0)
				fprintf(foutput, "\t%s\n", writem(u->pitem));
		}
	}

	/* check for state equal to another */

	TLOOP(j0) if ((j1 = temp1[j0]) != 0) {
		fprintf(foutput, "\n\t%s  ", symnam(j0));
		if (j1 > 0) {	/* shift, error, or accept */
			if (j1 == ACCEPTCODE)
				fprintf(foutput, "accept");
			else if (j1 == ERRCODE)
				fprintf(foutput, "error");
			else
				fprintf(foutput, "shift %d", j1);
		} else
			fprintf(foutput, "reduce %d", -j1);
	}

	/* output the final production */

	if (lastred)
		fprintf(foutput, "\n\t.  reduce %d\n\n", lastred);
	else
		fprintf(foutput, "\n\t.  error\n\n");

	/* now, output nonterminal actions */

	j1 = ntokens;
	for (j0 = 1; j0 <= nnonter; ++j0) {
		if (temp1[++j1])
			fprintf(foutput, "\t%s  goto %d\n", symnam(j0 + NTBASE), temp1[j1]);
	}

}

void wdef(char *s, int n)
{				/* output a definition of s to the value n */
	fprintf(ftable, "# define %s %d\n", s, n);
}

void warray(char *s, int *v, int n)
{

	int i;

	fprintf(ftable, "short %s[]={\n", s);
	for (i = 0; i < n;) {
		if (i % 10 == 0)
			fprintf(ftable, "\n");
		fprintf(ftable, "%4d", v[i]);
		if (++i == n)
			fprintf(ftable, " };\n");
		else
			fprintf(ftable, ",");
	}
}

void hideprod(void)
{
	/* in order to free up the mem and amem arrays for the optimizer, */
	/* and still be able to output yyr1, etc., after the sizes of */
	/* the action array is known, we hide the nonterminals */
	/* derived by productions in levprd. 
	 */

	int i, j;

	j = 0;
	levprd[0] = 0;
	PLOOP(1, i) {
		if (!(levprd[i] & REDFLAG)) {
			++j;
			if (foutput != NULL) {
				fprintf(foutput, "Rule not reduced:   %s\n", writem(prdptr[i]));
			}
		}
		levprd[i] = *prdptr[i] - NTBASE;
	}
	if (j)
		fprintf(stdout, "%d rules never reduced\n", j);
}
